package de.lns.level;

import de.lns.level.Circle;

import de.lns.level.Rectangle;
import de.lns.level.Triangle;
import de.lns.render.Drawable;
import de.lns.render.Model;
import de.lns.render.ModelFactory;

import org.ini4j.Ini;

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.nio.FloatBuffer;
import java.util.HashSet;
/**
 * 
 * @author Frederick Hastedt
 *
 */
public class Map implements Drawable{
	/*
	 * textureid: the id that's used if the texture has to be loaded more than once
	 * width: the width of the map in tiles
	 * height: the height of the map in tiles
	 * layers: the amount of layers the map has
	 * index: an iterator
	 * k: another iterator
	 * clip: determines whether the objects currently being read are clip or noclip objects
	 * 
	 * pathString: the String that is read from the .path file
	 * waveString: the String that is read from the .waves file
	 * shapeLayout: the String that is read from the .map file
	 * dataArray: temporary storage for the data from shapeLayout before it is actually used
	 * dataPosition: determines what type the currently read number is (x coord, y coord.. etc)
	 * path: holds the path in pairs of {X, Y
	 * waves: holds the waves in sets of {amount, type, time}
	 * 
	 * mapDimensions: holds the dimensions of the map
	 * spawnpoint: holds the X, Y and Z coordinates of the spawnpoint for the mobs
	 * destination: holds the X, Y and Z coordinates of the destination for the mobs
	 * clipObjects: a HashSet that saves the clipObjects for the map
	 * noClipObjects: a HashSet that saves the noClipObjects for the map
	 * 
	 * mr: FileReader for the map
	 * mapreader: BufferedReader for the map
	 * pr: FileReader for the path
	 * pathreader: BufferedReader for the path
	 * wr: FileReader for the waves
	 * wavereader: BufferedReader for the waves
	 */
	private static FloatBuffer mesh;
	private static FloatBuffer mapping;
	private int textureid;
	private int width;
	private int height;
	private int layers;
	private int index;
	private int k;
	private boolean clip;
	
	private String pathString;
	private String waveString;
	private String shapeLayout;
	private String[] dataArray = new String[6];
	private int dataPosition;
	private int[] path;
	private int[] waves;
	
	private int[] mapDimensions = new int [3];	
	private int[] spawnpoint = new int[3];
	private int[] destination = new int[3];
	private HashSet<Shapes> clipObjects = new HashSet<Shapes>();
	private HashSet<Shapes> noclipObjects = new HashSet<Shapes>();	

	private FileReader mr;
	private BufferedReader mapreader;
	
	private FileReader pr;
	private BufferedReader pathreader;
	
	private FileReader wr;
	private BufferedReader wavereader;
	private Model model;
	
	/**
	 * 
	 * @return  int array with X, Y and Z dimensions of the map
	 */
	public int[] getMapDimensions() {
		return mapDimensions;
	}
	
	/**
	 * 
	 * @return HashSet<Shapes> with the clipObjects of the map
	 */
	public HashSet<Shapes> getClipObjects() {
		return clipObjects;
	}
	
	/**
	 * 
	 * @return HashSet<Shapes> with the noClipObjects of the map
	 */
	public HashSet<Shapes> getNoClipObjects() {
		return noclipObjects;
	}
	
	/**
	 * 
	 * @return int array with X, Y and Z coordinates of the spawnpoint
	 */
	public int[] getSpawnpoint() {
		return spawnpoint;
	}
	
	/**
	 * 
	 * @return int array with X, Y and Z coordinates of the destination
	 */
	public int[] getDestination() {
		return destination;
	}
	
	/**
	 * 
	 * @return int array with the coordinates of the map in pairs
	 */
	public int[] getPath(){
		return path;
	}
	
	/**
	 * 
	 * @return int array with the waves in sets {amount, type, time}
	 */
	public int[] getWaves(){
		
		return waves;
	}
	
	/**
	 * 
	 * @param propertiepath: path of the .ini file for the map
	 * 
	 * 
	 */
	public Map(String propertiepath) throws IOException {
		
		
		/*
		 * opening the .ini file
		 */
		Ini ini = new Ini(new FileReader(propertiepath));
		
		/*
		 * opening the .map file
		 */
		mr = new FileReader(ini.get("paths", "map"));
		mapreader = new BufferedReader(mr);
		
		/*
		 * opening the .path file
		 */
		pr = new FileReader(ini.get("paths", "path"));
		pathreader = new BufferedReader(pr);
		
		/*
		 * opening the .waves file
		 */
		wr = new FileReader(ini.get("paths", "waves"));
		wavereader = new BufferedReader(wr);
		
		/*
		 * setup for the path, waves and clip/- & noClipObjects to be read
		 */
		shapeLayout = mapreader.readLine();
		pathString = pathreader.readLine();
		waveString = wavereader.readLine();
		path = new int[Integer.parseInt(ini.get("game", "PathPoints"))*2];
		waves = new int[Integer.parseInt(ini.get("game", "Waves"))*3];
		for(int i = 0; i<6; i++)
			dataArray[i]="";
		dataPosition = 0;
		clip = true;
		
		/*
		 * from 'textureid = ...' to 'mapDimension[2] = ...' 
		 * assigning values to the variables from the .ini file
		 */
		textureid = de.lns.render.TextureLoader.loadTexture(ini.get("paths", "texturepath"));
		
		width = Integer.parseInt(ini.get("dimensions", "width"));
		height = Integer.parseInt(ini.get("dimensions", "height"));
		layers = Integer.parseInt(ini.get("dimensions", "layers"));
		
		spawnpoint[0] = Integer.parseInt(ini.get("game", "SpawnX"));
		spawnpoint[1] = Integer.parseInt(ini.get("game", "SpawnY"));
		spawnpoint[2] = Integer.parseInt(ini.get("game", "SpawnZ"));
		
		destination[0] = Integer.parseInt(ini.get("game", "DestinationX"));
		destination[1] = Integer.parseInt(ini.get("game", "DestinationY"));
		destination[2] = Integer.parseInt(ini.get("game", "DestinationZ"));
		
		mapDimensions[0] = width;
		mapDimensions[1] = height;
		mapDimensions[2] = layers;
		
		mesh = ModelFactory.genRectMesh(769, 573, 384.5f, 286.5f);
		mapping = ModelFactory.genRectMap();
		
		model = ModelFactory.genModelSimple(this);
		
		createWaves();
		createPath();		
		createClipAndNoclipObjects();
									
	}
	
	/**
	 * Writes the waveString that was formerly created from the .waves file into an array.
	 */
	public void createWaves(){
		k = 0;
		index = 0;
		while(waveString.charAt(index)!='|'){
			if(waveString.charAt(index)==';'){				
				dataPosition = 0;
				waves[k] = Integer.parseInt(dataArray[0]);
				k++;
				
				for(int j = 0; j < 6; j++)
					dataArray[j]= "";	
				
				index++;
			}
			else{
				dataArray[dataPosition] += Character.toString(waveString.charAt(index));
				index++;
			}
		}	
	}
	
	/**
	 * Writes the pathString that was formerly created from the .path file into an array.
	 */
	public void createPath(){
		k = 0;
		index = 0;
		while(pathString.charAt(index)!='|'){
			if(pathString.charAt(index)==';'){
				dataPosition = 0;
				path[k] = Integer.parseInt(dataArray[0]);
				k++;
				
				for(int j = 0; j < 6; j++)
					dataArray[j] = "";
				
				index++;
			}
			else{
				dataArray[dataPosition] += Character.toString(pathString.charAt(index));
				index++;
			}
		}
	}

	public void createClipAndNoclipObjects(){
		index = 0;
		for(int i = 0; i < 2; i++){				
					/*
					 *  Loop for creating the clip and noclip Rectangles from the shapeLayout String and saving them.
					 */
					while(shapeLayout.charAt(index) != '|'){
						if(shapeLayout.charAt(index) == ';'){
							if(dataPosition < 3)
								dataPosition++;
							
							else{
								dataPosition = 0;
								if(clip){
									clipObjects.add(new Rectangle(Double.parseDouble(dataArray[0]), 
										Double.parseDouble(dataArray[1]), 
										Double.parseDouble(dataArray[2]), 
										Double.parseDouble(dataArray[3])));
								}
								else{
									noclipObjects.add(new Rectangle(Double.parseDouble(dataArray[0]), 
										Double.parseDouble(dataArray[1]), 
										Double.parseDouble(dataArray[2]), 
										Double.parseDouble(dataArray[3])));
								}
								
								for(int j = 0; j < 6; j++)
									dataArray[j] = "";
							}	
							index++;				
					}
					else{
						dataArray[dataPosition] += Character.toString(shapeLayout.charAt(index));
						index++;
					}
				}
				index++;
				
					/*
					 *  Loop for creating the clip and noclip Circles from the shapeLayout String and saving them.
					 */
					while(shapeLayout.charAt(index) != '|'){
						if(shapeLayout.charAt(index) == ';'){
							if(dataPosition < 2)
								dataPosition++;
							
							else{
								dataPosition = 0;
								if(clip){
									clipObjects.add(new Circle(Double.parseDouble(dataArray[0]), 
										Double.parseDouble(dataArray[1]), 
										Double.parseDouble(dataArray[2])));
								}
								else{
									noclipObjects.add(new Circle(Double.parseDouble(dataArray[0]), 
										Double.parseDouble(dataArray[1]), 
										Double.parseDouble(dataArray[2])));
								}
								for(int j = 0; j < 6; j++)
									dataArray[j] = "";
							}	
							index++;				
						}
						else{
							dataArray[dataPosition] += Character.toString(shapeLayout.charAt(index));
							index++;
						}
					}
				index++;
				
					/*
					 * Loop for creating the clip and noclip Triangles from the shapeLayout String and saving them.
					 */
					while(shapeLayout.charAt(index) != '|'){
						if(shapeLayout.charAt(index) == ';'){
							if(dataPosition < 5)
								dataPosition++;
							
							else{
								dataPosition = 0;
								if(clip){
									clipObjects.add(new Triangle(Double.parseDouble(dataArray[0]), 
											Double.parseDouble(dataArray[1]), 
											Double.parseDouble(dataArray[2]), 
											Double.parseDouble(dataArray[3]), 
											Double.parseDouble(dataArray[4]), 
											Double.parseDouble(dataArray[5])));
								}
								else{
									noclipObjects.add(new Triangle(Double.parseDouble(dataArray[0]), 
											Double.parseDouble(dataArray[1]), 
											Double.parseDouble(dataArray[2]), 
											Double.parseDouble(dataArray[3]), 
											Double.parseDouble(dataArray[4]), 
											Double.parseDouble(dataArray[5])));
								}
								for(int j = 0; j < 6; j++)
									dataArray[j] = "";
							}	
							index++;
							clip = false;
						}
						else{
							dataArray[dataPosition] += Character.toString(shapeLayout.charAt(index));
							index++;
						}
					}
				index++;
				}
	}
	
	@Override
	public Model model() {
		return model;
	}

	@Override
	public double getScale() {
		return 1;
	}

	@Override
	public double getPosX() {
		return 0;
	}

	@Override
	public double getPosY() {
		return 0;
	}

	@Override
	public double getPosZ() {
		return 0;
	}

	@Override
	public double getDirection() {
		return 0;
	}

	@Override
	public FloatBuffer getMesh() {
		return mesh;
	}

	@Override
	public FloatBuffer getMapping() {
		return mapping;
	}

	@Override
	public int getTextureID() {
		return textureid;
	}

	@Override
	public boolean alignRel() {
		return true;
	}


}